﻿Imports GMap.NET
Imports GMap.NET.WindowsForms
Imports GMap.NET.WindowsForms.Markers
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Data.Common
Imports System.ComponentModel
Imports WRDB.DataWeb
Imports WRDB.DataWeb.clsWebProvider

''' <summary>
''' This class acquires data from selected web services and directly imports into working table. References to WRDB.DataWeb and Google Map Control are required
''' Google Map Control: http://greatmaps.codeplex.com/ 
''' </summary>
Public Class frmDownload

#Region "Public properties..."

    ''' <summary>
    ''' Return number of data observations read
    ''' </summary>
    Public Property DataCount As Integer

    ''' <summary>
    ''' Return number of pcodes read
    ''' </summary>
    Public Property PCodeCount As Integer

    ''' <summary>
    ''' Return number of stations read
    ''' </summary>
    Public Property StationCount As Integer

#End Region

#Region "Private variables..."

    Const REGAPP = "WRDB"

    Const UseAsyncCalls As Boolean = True

    Friend Const HelpTopicID As Integer = 22

    ''' <summary>
    ''' Selected lat/long box on map
    ''' </summary>
    Private AreaOfInterest As RectangleF = Nothing

    ''' <summary>
    ''' WRDB Project instance
    ''' </summary>
    Private _Project As WRDB.Project.clsProject

    ''' <summary>
    ''' INI file instance
    ''' </summary>
    Private _IniFile As WRDB.Project.clsWRDBIniFile

    ''' <summary>
    ''' Dictionary containing lookup of FIPS code
    ''' Key: county ~ state
    ''' Value: FIPS code
    ''' </summary>
    Private _dictFIPS As Generic.Dictionary(Of String, String)

    ''' <summary>
    ''' Dictionary to lookup PCode given WQX info
    ''' Key: WQX_CharacteristicName;WQX_ResultSampFraction;WQX_ResultMeasureUnit
    ''' Value: PCode
    ''' </summary>
    Private _dictPCodeLookup As Generic.Dictionary(Of String, String)

    ''' <summary>
    ''' Specified date range from frmMapping
    ''' </summary>
    Dim MinDate, MaxDate As Date

    Private InfoText As String = "Select the desired data source and date range, then click the Select Area of Interest button and draw " &
                                    "latitude/longitude window by dragging while pressing the left mouse button to set the corners; " &
                                    "pan and zoom using the zoom control in the upper-left part of the map, by using the mouse wheel, " &
                                    "or by using Shift-drag to zoom to window. Available data networks are listed to the right and all " &
                                    "stations associated with the checked networks are shown on the map. Select stations by clicking on symbols or using the Stations tab."

    ''' <summary>
    ''' There are lots of images in resources like "blue_MarkerA.png" used to differentiate stations from different CUAHSI networks; set unique set of corresponding colors
    ''' </summary>
    Dim lstColors() As String = {"blue", "brown", "darkgreen", "green", "orange", "paleblue", "pink", "purple", "red", "yellow"}

    ''' <summary>
    ''' Contains all map images in resources file
    ''' </summary>
    Private MapImageList As New ImageList

    Private WebProvider As WRDB.DataWeb.clsWebProvider

#End Region

#Region "Form-level events..."

    ' ''' <summary>
    ' ''' Instantiate form
    ' ''' </summary>
    'Public Sub New()

    '    ' This call is required by the Windows Form Designer.
    '    InitializeComponent()

    '    ' Add any initialization after the InitializeComponent() call.
    '    _dictFIPS = Nothing

    'End Sub

    ''' <summary>
    ''' Instantiate form
    ''' </summary>
    ''' <param name="IniFile">Configuration file</param>
    ''' <param name="Project">WRDB Project</param>
    ''' <remarks></remarks>
    Public Sub New(IniFile As Project.clsWRDBIniFile, ByVal Project As WRDB.Project.clsProject)

        ' This call is required by the Windows Form Designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.

        _IniFile = IniFile
        _Project = Project

        'county ~ state is key, FIPS code is value
        _dictFIPS = New Generic.Dictionary(Of String, String)
        Dim dr As DbDataReader = Project.DB.ExecuteReader("SELECT County_Name,State,FIPS_Code FROM FIPSCodes")
        While dr IsNot Nothing AndAlso dr.Read
            _dictFIPS.Add(String.Format("{0} ~ {1}", TestNull(dr.GetValue(0), ""), TestNull(dr.GetValue(1), "")).ToUpper, TestNull(dr.GetValue(2), ""))
        End While
        dr.Close()

        'create lookup dictionary for PCodes
        _dictPCodeLookup = New Generic.Dictionary(Of String, String)
        dr = Project.DB.ExecuteReader("SELECT PCodes.PCode,PCodes.WQX_CharacteristicName,PCodes.WQX_ResultSampFraction,PCodes.WQX_ResultMeasureUnit FROM PCodes")
        While dr IsNot Nothing And dr.Read
            If Not (dr.IsDBNull(0) Or dr.IsDBNull(1)) Then
                Dim key As String = dr.GetString(1).Trim & ";" & TestNull(dr.GetValue(2), "") & ";" & TestNull(dr.GetValue(3), "")
                If Not _dictPCodeLookup.ContainsKey(key) Then _dictPCodeLookup.Add(key, dr.GetString(0))
            End If
        End While
        dr.Close()

    End Sub

    Private Sub frmDownload_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
        SaveWindowPos(REGAPP, Me)
        SaveControlValues(REGAPP, Me)
        SaveSetting(REGAPP, Name, lstNCDCCountry.Name, lstNCDCCountry.Text)
        SaveSetting(REGAPP, Name, lstNCDCState.Name, lstNCDCState.Text)
        SaveSetting(REGAPP, Name, "MapLat", Map.Position.Lat)
        SaveSetting(REGAPP, Name, "MapLon", Map.Position.Lng)
        SaveSetting(REGAPP, Name, "Zoom", Map.Zoom)
    End Sub

    Private Sub frmDownload_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Try
            Map.Visible = False
            With lstDataSource.Items
                .Add("USEPA STORET/WQX Water Quality")
                .Add("USGS NWIS Water-Quality")
                .Add("USGS NWIS Daily Values")
                .Add("USGS NWIS Real-time Instantaneous")
                .Add("NOAA NCDC Meteorologic Data")
                .Add("CUASHI HIS Central (Multiple Networks)")
                .Add("Georgia EPD ICIS/NPDES DMR Data")
            End With
            lstDataSource.SelectedIndex = 0

            GetWindowPos(REGAPP, Me)
            With cboMapType
                .DropDownStyle = ComboBoxStyle.DropDownList
                .MaxDropDownItems = 50
                .Sorted = True
                .Items.Clear()
                    'only add a subset of all available maps (too many!)
                For Each mp As MapProviders.GMapProvider In MapProviders.GMapProviders.List
                    Dim s As String = mp.Name
                    If (s.ToUpper.StartsWith("ARCGIS") Or
                        s.ToUpper.StartsWith("BING") Or
                        s.ToUpper.StartsWith("GOOGLE") Or
                        s.ToUpper.StartsWith("OPENSTREET") Or
                        s.ToUpper.StartsWith("OVI") Or
                        s.ToUpper.StartsWith("YAHOO")) AndAlso
                        Not (s.ToUpper.Contains("CHINA") Or
                             s.ToUpper.Contains("KOREA")) Then
                        .Items.Add(s.Replace("_", " "))
                    End If
                Next
            End With

            Try
                GetControlValues(REGAPP, Me)
            Catch ex As Exception
            End Try

            For Each key As String In clsWebProvider.GetCountryCodes.Keys
                lstNCDCCountry.Items.Add(Strings.StrConv(key, VbStrConv.ProperCase))
            Next
            lstNCDCCountry.Text = GetSetting(REGAPP, Me.Name, lstNCDCCountry.Name, "United States")
            For Each key As String In clsWebProvider.GetStateCodes.Keys
                lstNCDCState.Items.Add(key)
            Next
            lstNCDCState.Sorted = True
            lstNCDCState.Text = GetSetting(REGAPP, Me.Name, lstNCDCState.Name, "Georgia")
            With lstNCDCDataset.Items
                .Clear()
                .Add("Daily")
                .Add("Integrated Surface Hourly")
                .Add("Integrated Surface Daily")
            End With
            lstNCDCDataset.SelectedIndex = 0

            With Map
                .Position = (New PointLatLng(GetSetting(REGAPP, Name, "MapLat", 51),
                                             GetSetting(REGAPP, Name, "MapLon", -132)))
                .MinZoom = 2
                .MaxZoom = 15
                trkZoom.Maximum = .MaxZoom - .MinZoom
                .Zoom = GetSetting(REGAPP, Name, "Zoom", 4)
                .MouseWheelZoomType = MouseWheelZoomType.ViewCenter
                .IgnoreMarkerOnMouseWheel = True
                .DragButton = Windows.Forms.MouseButtons.Middle

                lyrRect = New GMapOverlay(Map, "Rect")
                Map.Overlays.Add(lyrRect)
                If IsNumeric(txtLatMax.Text) AndAlso IsNumeric(txtLatMin.Text) AndAlso IsNumeric(txtLonMax.Text) AndAlso IsNumeric(txtLonMin.Text) Then
                    DrawRect(New PointLatLng(txtLatMax.Text, txtLonMin.Text), New PointLatLng(txtLatMin.Text, txtLonMax.Text))
                End If

                lyrStations = New GMapOverlay(Map, "Stations")
                Map.Overlays.Add(lyrStations)
                Map.Visible = True
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub
#End Region

#Region "Event handlers for form controls..."

    Private LastItemClicked As ListViewItem = Nothing

    Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click
        Me.DialogResult = System.Windows.Forms.DialogResult.Cancel
        Me.Close()
    End Sub

    Private Sub btnOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOK.Click
        If GetResults() Then Me.DialogResult = Windows.Forms.DialogResult.OK
    End Sub

    Private Sub btnHelp_Click(sender As System.Object, e As System.EventArgs) Handles btnHelp.Click
        ShowHelp(Me, HelpTopicID)
    End Sub

    Private Sub btnSetAOI_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnSetAOI.Click
        AOIButtonClicked = True
        btnSetAOI.Visible = False
        lblSetAOI.Visible = True
        lyrRect.Polygons.Clear()
        AreaOfInterest.Width = 0
    End Sub

    Private Sub lblSetAOI_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblSetAOI.Click
        AOIButtonClicked = False
        btnSetAOI.Visible = True
        lblSetAOI.Visible = False
        RectActive = False
        lyrRect.Polygons.Clear()
        AreaOfInterest.Width = 0
        lyrStations.Markers.Clear()
    End Sub

    Private Sub cboMapType_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles cboMapType.SelectedIndexChanged
        For Each mp As MapProviders.GMapProvider In MapProviders.GMapProviders.List
            If mp.Name = cboMapType.Text.Replace(" ", "_") Then
                Map.MapProvider = mp
                Exit For
            End If
        Next
        Map.Focus()
    End Sub

    Private Sub chkTooltips_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles chkTooltips.CheckedChanged
        If lyrStations IsNot Nothing Then
            For Each m As GMapMarker In lyrStations.Markers
                m.ToolTipMode = IIf(chkTooltips.Checked, MarkerTooltipMode.OnMouseOver, MarkerTooltipMode.Never)
            Next
        End If
    End Sub

    Private Sub dt_Validated(ByVal sender As Object, ByVal e As System.EventArgs) Handles dtEnd.Validated, dtStart.Validated
        'if Cuahsi, change of dates requires reselection of available series, etc.
        If WebProvider IsNot Nothing AndAlso WebProvider.ProviderType = enumProviderType.Cuahsi Then ClearAOI() : ClearStations()
        lvParameters.Items.Clear() 'to force refresh
    End Sub

    Private Sub lblDataSource_Click(sender As System.Object, e As System.EventArgs) Handles lblDataSource.Click
        If WebProvider IsNot Nothing Then Process.Start(WebProvider.URL)
    End Sub

    Private Sub lnkCancel_LinkClicked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.LinkLabelLinkClickedEventArgs) Handles lnkCancel.LinkClicked
        WebProvider.Cancel()
    End Sub

    Private Sub lstDataSource_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles lstDataSource.SelectedIndexChanged
        Try
            ClearStations()
            If lstDataSource.SelectedIndex = -1 Then Exit Sub
            WebProvider = clsWebProvider.GetWebProvider(lstDataSource.SelectedIndex)
            WebProvider.AreaOfInterest = AreaOfInterest

            If WebProvider Is Nothing Then
                If Not Debugger.IsAttached Then
                    WarningMsg("This data source is not yet implemented.")
                    lstDataSource.SelectedIndex = 0
                    Exit Sub
                End If
            End If

            Select Case WebProvider.ProviderType
                Case enumProviderType.Cuahsi
                    If Not tabSelect.TabPages.Contains(TabPageNetworks) Then tabSelect.TabPages.Insert(1, TabPageNetworks)
                    If Not tabSelect.TabPages.Contains(TabPageSeries) Then tabSelect.TabPages.Insert(tabSelect.TabPages.Count, TabPageSeries)
                    If tabSelect.TabPages.Contains(TabPageNCDC) Then
                        tabSelect.TabPages.Remove(TabPageNCDC)
                    End If
                Case enumProviderType.Ncdc
                    If tabSelect.TabPages.Contains(TabPageNetworks) Then tabSelect.TabPages.Remove(TabPageNetworks)
                    If tabSelect.TabPages.Contains(TabPageSeries) Then tabSelect.TabPages.Remove(TabPageSeries)
                    If Not tabSelect.TabPages.Contains(TabPageNCDC) Then
                        tabSelect.TabPages.Insert(1, TabPageNCDC)
                    End If
                Case Else
                    If tabSelect.TabPages.Contains(TabPageNetworks) Then tabSelect.TabPages.Remove(TabPageNetworks)
                    If tabSelect.TabPages.Contains(TabPageSeries) Then tabSelect.TabPages.Remove(TabPageSeries)
                    If tabSelect.TabPages.Contains(TabPageNCDC) Then
                        tabSelect.TabPages.Remove(TabPageNCDC)
                    End If
            End Select

            lblDataSource.Text = WebProvider.Description
            FillNetworkList()

        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub lstNCDCCountry_SelectedIndexChanged(sender As Object, e As System.EventArgs) Handles lstNCDCCountry.SelectedIndexChanged
        lstNCDCState.Enabled = (lstNCDCCountry.Text.ToUpper = "UNITED STATES")
    End Sub

    Private Sub lstNCDC_SelectedIndexChanged(sender As Object, e As System.EventArgs) Handles lstNCDCDataset.SelectedIndexChanged, lstNCDCCountry.SelectedIndexChanged, lstNCDCState.SelectedIndexChanged
        If lstNCDCDataset.SelectedIndex >= 0 AndAlso lstNCDCCountry.SelectedIndex >= 0 AndAlso lstNCDCState.SelectedIndex >= 0 Then
            FillStationList()
            DrawStations()
        End If
    End Sub

    Private Sub lv_MouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles lvNetworks.MouseClick, lvParameters.MouseClick, lvStations.MouseClick, lvSeries.MouseClick
        LastItemClicked = CType(sender, WRDB.Controls.MyListView).GetItemAt(e.X, e.Y)
    End Sub

    Private Sub lvNetworks_ItemChecked(ByVal sender As Object, ByVal e As System.Windows.Forms.ItemCheckedEventArgs) Handles lvNetworks.ItemChecked
        If lvNetworks.Visible Then
            lvNetworks.SelectedItems.Clear()
            e.Item.Selected = True
            Application.DoEvents()
            FillStationList()
            DrawStations()
        End If
    End Sub

    Private Sub lvNetworks_ItemSelectionChanged(sender As Object, e As System.Windows.Forms.ListViewItemSelectionChangedEventArgs) Handles lvNetworks.ItemSelectionChanged
        UpdateNetworkDetails()
    End Sub

    Private Sub lvParameter_ItemChecked(ByVal sender As Object, ByVal e As System.Windows.Forms.ItemCheckedEventArgs) Handles lvParameters.ItemChecked
        If lvParameters.Visible Then
            FillSeriesList()
        End If
    End Sub

    Private Sub lvSeries_ItemChecked(ByVal sender As Object, ByVal e As System.Windows.Forms.ItemCheckedEventArgs) Handles lvSeries.ItemChecked
        If lvSeries.Visible Then
            e.Item.Selected = True
            UpdateResultCount(True)
            ShowProgress(False)
        End If
    End Sub

    Private Sub lvSeries_ItemSelectionChanged(ByVal sender As Object, ByVal e As System.Windows.Forms.ListViewItemSelectionChangedEventArgs) Handles lvSeries.ItemSelectionChanged
        UpdateSeriesDetails()
    End Sub

    Private Sub UpdateNetworkDetails()
        Try
            If lvNetworks.SelectedItems.Count = 0 Then Exit Sub
            For Each n As HISCentral.ServiceInfo In WebProvider.dictNetworks.Values
                If n.Title = lvNetworks.SelectedItems(0).Text Then
                    With lvNetworkInfo
                        .Clear()
                        .Sorting = SortOrder.None
                        .CheckBoxes = False
                        .AddColumns("Item", "Value")
                        .HeaderStyle = ColumnHeaderStyle.None
                        .FullRowSelect = False
                        .AddRow("", "Name:", n.NetworkName)
                        '.AddRow("", "ID:", n.ServiceID)
                        .AddRow("", "Title:", n.Title)
                        .AddRow("", "Citation:", n.citation)
                        .AddRow("", "Abstract:", n.aabstract)
                        .AddRow("", "Organization:", n.organization)
                        .AddRow("", "Website:", n.orgwebsite)
                        .AddRow("", "Email:", n.Email)
                        .AddRow("", "Phone:", n.phone)
                        .AddRow("", "Description URL:", n.ServiceDescriptionURL)
                        .AddRow("", "Service URL:", n.servURL)
                        .AddRow("", "Status:", n.serviceStatus)
                        .AddRow("", "No. Sites:", n.sitecount)
                        .AddRow("", "No. Parms:", n.variablecount)
                        .AddRow("", "No. Values:", n.valuecount)
                        .AutoResizeColumns(True)
                    End With
                    Exit For
                End If
            Next
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub UpdateSeriesDetails()
        Try
            If lvSeries.SelectedItems.Count = 0 Then Exit Sub
            Dim ser As HISCentral.SeriesRecord = lvSeries.SelectedItems(0).Tag
            With lvSeriesInfo
                .Clear()
                .Sorting = SortOrder.None
                .CheckBoxes = False
                .AddColumns("Item", "Value")
                .HeaderStyle = ColumnHeaderStyle.None
                .FullRowSelect = False
                .AddRow("", "Network:", ser.ServCode)
                .AddRow("", "Station ID:", ser.location)
                .AddRow("", "Station Name:", ser.Sitename)
                .AddRow("", "Parm Code:", ser.VarCode)
                .AddRow("", "Parm Name:", ser.VarName)
                .AddRow("", "Medium:", ser.samplemedium)
                .AddRow("", "Value Type:", ser.valuetype)
                .AddRow("", "Time Units:", ser.timeunits)
                .AddRow("", "Data Type:", ser.datatype)
                .AddRow("", "Begin Date:", ser.beginDate)
                .AddRow("", "End Date:", ser.endDate)
                .AddRow("", "No. Values:", ser.ValueCount)
                .AutoResizeColumns(True)
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub lvStations_ItemChecked(ByVal sender As Object, ByVal e As System.Windows.Forms.ItemCheckedEventArgs) Handles lvStations.ItemChecked
        If lvStations.Visible Then
            lvParameters.Items.Clear()
            DrawStations()
            If WebProvider.ProviderType = enumProviderType.Cuahsi Then
                FillParameterList()
                FillSeriesList()
            End If
        End If
    End Sub

    Private Sub mnuSelect_Click(sender As Object, e As System.EventArgs) Handles mnuSelectAll.Click, mnuSelectNone.Click, mnuSelectOne.Click
        Try
            Dim cntl As Control = CType(CType(sender, ToolStripItem).Owner, ContextMenuStrip).SourceControl

            If TypeOf cntl Is WRDB.Controls.MyListView Then
                With CType(cntl, WRDB.Controls.MyListView)
                    .Visible = False
                    If sender Is mnuSelectOne Then
                        If LastItemClicked IsNot Nothing Then
                            .CheckAll(False)
                            LastItemClicked.Checked = True
                        End If
                    Else
                        .CheckAll(sender Is mnuSelectAll)
                    End If
                    .Visible = True
                    .Refresh()
                    Application.DoEvents()
                    If cntl Is lvNetworks Then
                        FillStationList()
                    ElseIf cntl Is lvStations Then
                        lvParameters.Items.Clear()
                        DrawStations()
                        If WebProvider.ProviderType = enumProviderType.Cuahsi Then
                            FillParameterList()
                            FillSeriesList()
                        End If
                    ElseIf cntl Is lvParameters Then
                        FillSeriesList()
                    End If
                    LastItemClicked = Nothing
                End With
            ElseIf TypeOf cntl Is CheckedListBox Then
                With CType(cntl, CheckedListBox)
                    .Visible = False
                    For i As Integer = 0 To .Items.Count - 1
                        .SetItemChecked(i, sender Is mnuSelectAll)
                    Next
                    .Visible = True
                    FillStationList()
                End With
            End If
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuMap_ItemClicked(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuMapSelectAll.Click, mnuMapSelectNone.Click
        With lvStations
            .Visible = False
            .CheckAll(sender Is mnuMapSelectAll)
            .Visible = True
            DrawStations()
            If WebProvider.ProviderType = enumProviderType.Cuahsi Then FillParameterList()
        End With
    End Sub

    Private Sub mnuMapFind_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuMapFind.Click
        Dim s As String = InputBox("Enter the place name you wish to find:", "Find Location")
        If s <> "" Then
            'first search for station ID
            If WebProvider.dictStations.ContainsKey(s) Then
            Else
                Dim ret As GeoCoderStatusCode = Map.SetCurrentPositionByKeywords(s)
                Dim errMsg As String = Strings.StrConv([Enum].GetName(GetType(GeoCoderStatusCode), ret).Replace("G_GEO_", ""), VbStrConv.ProperCase).Replace("_", " ")
                If ret <> GeoCoderStatusCode.G_GEO_SUCCESS Then
                    WarningMsg("Unable to find location; error message was: " & errMsg)
                End If
            End If
        End If
    End Sub

    Private Sub mnuMapZoom_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuMapZoom.Click
        If AreaOfInterest.IsEmpty Then Exit Sub
        With AreaOfInterest
            Map.SetZoomToFitRect(New GMap.NET.RectLatLng(.Top, .Left, .Width, -.Height))
        End With
    End Sub

    Private Sub tabSelect_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles tabSelect.SelectedIndexChanged
        Try
            If (AreaOfInterest.IsEmpty And tabSelect.SelectedIndex > 0) Or (lvStations.Items.Count = 0 And tabSelect.SelectedIndex > 1) Then
                tabSelect.SelectedIndex = 0
                Application.DoEvents()
                WarningMsg("Please select your desired Data Source and date range, and then specify your area of interest on the map by clicking the Set Area of Interest button (or by holding the Control key) and dragging the corners of a box on the map until one or more stations is shown.")
            ElseIf tabSelect.SelectedTab Is TabPageParameters Then
                If WebProvider.ProviderType = enumProviderType.Cuahsi Or lvParameters.Items.Count = 0 Then FillParameterList()
            ElseIf tabSelect.SelectedTab Is TabPageSeries Then
                If lvParameters.Items.Count = 0 Then FillParameterList()
                FillSeriesList()
            End If
            If tabSelect.SelectedIndex = 4 Then lvSeries.Select()
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub txtFilterParmName_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles txtFilterParmName.TextChanged
        With txtFilterParmName
            If .Visible Then
                .AcceptsReturn = True
                .Multiline = True
                .WordWrap = False
                If .Text.EndsWith(vbCrLf) Then
                    .Text = .Text.Replace(vbCrLf, "")
                    .Enabled = False
                    FillParameterList()
                    FillSeriesList()
                    .Enabled = True
                    .Focus()
                End If
            End If
        End With
    End Sub

    Private Sub txtFilterStaName_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles txtFilterStaName.TextChanged
        With txtFilterStaName
            If .Visible Then
                .AcceptsReturn = True
                .Multiline = True
                .WordWrap = False
                If .Text.EndsWith(vbCrLf) Then
                    .Text = .Text.Replace(vbCrLf, "")
                    .Enabled = False
                    'FillStationsCompletedFinal()
                    FillStationList()
                    .Enabled = True
                    .Focus()
                End If
            End If
        End With
    End Sub

    Private Sub SetAllChecks(ByVal Node As TreeNode, ByVal Checked As Boolean)
        Node.Checked = Checked
        For Each c As TreeNode In Node.Nodes
            SetAllChecks(c, Checked)
        Next
    End Sub

    Private Sub tvParameter_AfterCheck(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewEventArgs) Handles tvParameter.AfterCheck
        If e.Action = TreeViewAction.ByKeyboard Or e.Action = TreeViewAction.ByMouse Then SetAllChecks(e.Node, e.Node.Checked)
    End Sub

#End Region

#Region "Map control and drawing methods and events..."

    Private LatLonPt1, LatLonPt2 As PointLatLng

    Private lyrRect, lyrStations As GMapOverlay
    Private RectActive As Boolean = False
    Private AOIButtonClicked As Boolean = False

    Private Sub DrawRect(ByVal LatLon1 As PointLatLng, ByVal LatLon2 As PointLatLng)
        Dim lstPoints As New Generic.List(Of PointLatLng)
        lstPoints.Add(New PointLatLng(LatLon1.Lat, LatLon1.Lng))
        lstPoints.Add(New PointLatLng(LatLon2.Lat, LatLon1.Lng))
        lstPoints.Add(New PointLatLng(LatLon2.Lat, LatLon2.Lng))
        lstPoints.Add(New PointLatLng(LatLon1.Lat, LatLon2.Lng))
        lstPoints.Add(New PointLatLng(LatLon1.Lat, LatLon1.Lng))
        lyrRect.Polygons.Clear()
        Dim poly As New GMapPolygon(lstPoints, "Rect")
        poly.Fill = Brushes.Transparent
        Dim Pen As New Pen(Brushes.Red, 3)
        Pen.DashStyle = Drawing2D.DashStyle.DashDot
        poly.Stroke = Pen
        lyrRect.Polygons.Add(poly)
    End Sub

    Private Sub DrawStations()
        Try
            'already have dictionary of providers and stations; assign color and icon letter to agency based on provider (network)
            Dim c As Integer = 0
            Dim lstLtr As New Generic.List(Of String) 'if just take first letter, many NWIS services will have N, try to grab unique letters from subsequent names in list
            For Each kv As KeyValuePair(Of String, WRDB.DataWeb.clsWebProvider.clsAgency) In WebProvider.dictAgencies
                kv.Value.Color = lstColors(c Mod lstColors.Length)
                Dim ltr As Char = "A"
                If String.IsNullOrEmpty(kv.Value.AgencyName) Then
                    If Not String.IsNullOrEmpty(kv.Value.AgencyCode) Then ltr = kv.Value.AgencyCode.ToUpper.Substring(0, 1)
                Else
                    For Each s As String In kv.Value.AgencyName.Split(" ")
                        Dim testLtr As String = s.ToUpper.Substring(0, 1)
                        If Not lstLtr.Contains(testLtr) Then
                            ltr = testLtr
                            Exit For
                        End If
                    Next
                End If
                If ltr < "A" Or ltr > "Z" Then ltr = "A"
                If Not lstLtr.Contains(ltr) Then lstLtr.Add(ltr)
                kv.Value.Letter = ltr
                c += 1
            Next

            MapImageList.Images.Clear()

            If lyrStations IsNot Nothing Then lyrStations.Markers.Clear()

            'get agencies from list of available stations
            Dim lstAgencyName As New Generic.List(Of String)
            For i As Integer = 0 To lvStations.Items.Count - 1
                'Dim AgencyName As String = GetStation(i).AgencyName
                Dim AgencyName As String = WebProvider.dictStations(lvStations.Items(i).Text).AgencyName
                If Not lstAgencyName.Contains(AgencyName) Then lstAgencyName.Add(AgencyName)
            Next

            For i As Integer = 0 To Math.Min(1000, lvStations.Items.Count) - 1
                Dim sta As clsStation = GetStation(i)
                Dim img As String = "black_MarkerX"
                If lvStations.Items(i).Checked Then
                    img = String.Format("{0}_Marker{1}", WebProvider.dictAgencies(sta.AgencyCode).Color, WebProvider.dictAgencies(sta.AgencyCode).Letter)

                    If Not MapImageList.Images.ContainsKey(sta.AgencyCode) Then
                        Dim rm As Resources.ResourceManager = My.Resources.ResourceManager
                        Dim sym As Image = rm.GetObject(img)
                        MapImageList.Images.Add(sta.AgencyCode, sym)
                    End If
                End If
                lyrStations.Markers.Add(New GMapMarkerStation(New PointLatLng(sta.Latitude, sta.Longitude), 0.75, sta.StationID & vbCrLf & sta.StationName, img))
                lyrStations.Markers(i).ToolTipMode = IIf(chkTooltips.Checked, MarkerTooltipMode.OnMouseOver, MarkerTooltipMode.Never)
                'ShowProgress(i, lvStations.Items.Count - 1, "Drawing stations...")
            Next

            'now assign images to listview entries
            lvNetworks.SmallImageList = MapImageList
            For i As Integer = 0 To lvNetworks.Items.Count - 1
                With lvNetworks.Items(i)
                    .ImageKey = .Tag
                End With
            Next
            lvNetworks.AutoResizeColumns(True)
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    ''' <summary>
    ''' Clear areaofinterest box and all station markers
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub ClearStations()
        MapImageList.Images.Clear()
        If lyrStations IsNot Nothing Then lyrStations.Markers.Clear()
    End Sub

    ''' <summary>
    ''' Clear areaofinterest box and all station markers
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub ClearAOI()
        If lyrRect IsNot Nothing Then lyrRect.Polygons.Clear()
        AreaOfInterest.Width = 0
    End Sub

    Private Sub Map_MouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Map.MouseClick
        If Map.IsMouseOverMarker Then
            For i As Integer = 0 To lyrStations.Markers.Count - 1
                If lyrStations.Markers(i).IsMouseOver Then
                    lvStations.Items(i).Checked = Not lvStations.Items(i).Checked
                End If
            Next
            DrawStations()
            If WebProvider.ProviderType = enumProviderType.Cuahsi Then FillParameterList()
            UpdateResultCount(True)
            ShowProgress(False)
        End If
    End Sub

    Private Sub Map_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Map.MouseDown
        If e.Button = Windows.Forms.MouseButtons.Left AndAlso (Control.ModifierKeys = Keys.Control Or AOIButtonClicked) Then
            With Map
                LatLonPt1 = .FromLocalToLatLng(e.X, e.Y)
                RectActive = True
            End With
        End If
    End Sub

    Private Sub Map_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Map.MouseMove
        If RectActive AndAlso e.Button = Windows.Forms.MouseButtons.Left AndAlso (Control.ModifierKeys = Keys.Control Or AOIButtonClicked) Then
            With Map
                LatLonPt2 = .FromLocalToLatLng(e.X, e.Y)
                DrawRect(LatLonPt1, LatLonPt2)
            End With
        End If
    End Sub

    Private Sub Map_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Map.MouseUp
        If RectActive AndAlso e.Button = Windows.Forms.MouseButtons.Left Then
            AOIButtonClicked = False
            lblSetAOI.Visible = False
            btnSetAOI.Visible = True
            txtLatMin.Text = Math.Round(Math.Min(LatLonPt1.Lat, LatLonPt2.Lat), 4)
            txtLatMax.Text = Math.Round(Math.Max(LatLonPt1.Lat, LatLonPt2.Lat), 4)
            txtLonMin.Text = Math.Round(Math.Min(LatLonPt1.Lng, LatLonPt2.Lng), 4)
            txtLonMax.Text = Math.Round(Math.Max(LatLonPt1.Lng, LatLonPt2.Lng), 4)
            RectActive = False

            With AreaOfInterest
                .X = txtLonMin.Text
                .Width = txtLonMax.Text - .X
                .Y = txtLatMin.Text
                .Height = txtLatMax.Text - .Y
                'tabSelect.SelectedIndex = 0
            End With
            lblInfo.Visible = False
            pnlResults.Visible = True
            FillNetworkList()
        End If
    End Sub

    Private Sub Map_OnMapZoomChanged() Handles Map.OnMapZoomChanged
        With Map
            trkZoom.Value = .Zoom - .MinZoom
        End With
    End Sub

    Private Sub trkZoom_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles trkZoom.ValueChanged
        With Map
            .Zoom = trkZoom.Value + .MinZoom
        End With
    End Sub

    Private Sub picPanUp_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles picPanUp.Click
        With Map
            .Position = New PointLatLng(.Position.Lat + .CurrentViewArea.HeightLat / 2, .Position.Lng)
        End With
    End Sub

    Private Sub picPanDn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles picPanDn.Click
        With Map
            .Position = New PointLatLng(.Position.Lat - .CurrentViewArea.HeightLat / 2, .Position.Lng)
        End With
    End Sub

    Private Sub picPanLt_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles picPanLt.Click
        With Map
            .Position = New PointLatLng(.Position.Lat, .Position.Lng - .CurrentViewArea.WidthLng / 2)
        End With
    End Sub

    Private Sub picPanRt_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles picPanRt.Click, PictureBox1.Click
        With Map
            .Position = New PointLatLng(.Position.Lat, .Position.Lng + .CurrentViewArea.WidthLng / 2)
        End With
    End Sub

#End Region

#Region "Progress routines..."

    ''' <summary>
    ''' Used to delay display of progressbar to eliminate flashing
    ''' </summary>
    Private WithEvents ProgressTimer As New Timer

    Private Cancelled As Boolean

    Private Function ShowProgress(ByVal ValueOrIncrement As Integer, Optional ByVal MaxValue As Integer = -1, Optional ByVal Text As String = "") As Boolean
        If MaxValue = -1 Then
            ProgressBar1.Value = Math.Min(ProgressBar1.Maximum, ProgressBar1.Value + ValueOrIncrement)
        Else
            ProgressBar1.Maximum = MaxValue
            ProgressBar1.Value = Math.Min(ProgressBar1.Maximum, ValueOrIncrement)
        End If
        lblProgress.Text = Text
        ProgressTimer.Interval = 1000
        ProgressTimer.Enabled = True
        Application.DoEvents()
        Return Not Cancelled
    End Function

    Private Sub ProgressTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles ProgressTimer.Tick
        Dim ShowIt As Boolean = True
        pnlProgress.Visible = ShowIt
        lnkCancel.Visible = ShowIt
        lblInfo.Visible = Not ShowIt And AreaOfInterest.IsEmpty
        pnlResults.Visible = Not ShowIt And Not AreaOfInterest.IsEmpty
        Me.Cursor = IIf(ShowIt, Cursors.WaitCursor, Cursors.Default)
        ProgressTimer.Enabled = False
    End Sub

    ''' <summary>
    ''' Show or hide progressbar panel consisting of incrementing bar, marquee bar, and cancel link
    ''' </summary>
    ''' <param name="ShowIt">True or false to show/hide</param>
    ''' <param name="MaxValue">If showing, can set maximum value here; if omitted, will not show incrementing bar</param>
    Private Sub ShowProgress(ByVal ShowIt As Boolean, Optional ByVal MaxValue As Integer = -1, Optional ByVal Text As String = "")
        Static LastTimeHidden As Date = Date.MinValue 'so progress for immediately subsequent operations stay visible
        Try
            Cancelled = False
            ProgressBar1.Value = 0
            If Text = "" Then Text = "Initializing..."
            lblProgress.Text = Text
            If MaxValue > 0 Then
                ProgressBar1.Maximum = MaxValue + 1
            End If
            If ShowIt And Now.Subtract(LastTimeHidden).Seconds > 1 Then
                ProgressTimer.Interval = 1000
                ProgressTimer.Enabled = True
            Else
                ProgressTimer.Enabled = False
                pnlProgress.Visible = ShowIt
                lblInfo.Visible = Not ShowIt And AreaOfInterest.IsEmpty
                pnlResults.Visible = Not ShowIt And Not AreaOfInterest.IsEmpty
                Me.Cursor = IIf(ShowIt, Cursors.WaitCursor, Cursors.Default)
                LastTimeHidden = Now
            End If
            Application.DoEvents()
        Catch ex As Exception
            'if cancel async may throw exception here
        End Try
    End Sub

#End Region

#Region "Fill list methods..."

    Private Sub FillNetworkList()
        Try
            With AreaOfInterest
                If .IsEmpty OrElse .X = 0 OrElse .Y = 0 OrElse .Width = 0 OrElse .Height = 0 Then Exit Sub
            End With

            ShowProgress(True, , "Retrieving available networks...")

            'setting AOI for Cuahsi causes the list of networks to be determined too

            With WebProvider
                If UseAsyncCalls Then
                    Dim th As New Threading.Thread(New Threading.ParameterizedThreadStart(AddressOf .SetAreaOfInterest))
                    th.Start(AreaOfInterest)
                    While th.IsAlive
                        Threading.Thread.Sleep(200)
                        Application.DoEvents()
                    End While
                Else
                    .SetAreaOfInterest(AreaOfInterest)
                End If
                If .LastException IsNot Nothing Then Throw .LastException
            End With

            With lvNetworks
                .Visible = False
                .Sorting = SortOrder.Ascending
                .MultiSelect = False
                .FullRowSelect = True
                .HideSelectedRow = False
                .CheckOnClick = False
                .Clear()
                .AddColumns("Name")
                For Each n As DataWeb.HISCentral.ServiceInfo In WebProvider.dictNetworks.Values
                    With n
                        lvNetworks.AddRow(.NetworkName, .Title)
                    End With
                Next
                .AutoResizeColumns(True)
                .Visible = True
                .Enabled = True
                lblNumNetworks.Text = String.Format("Available Networks: {0}", lvNetworks.Items.Count)
            End With

            FillStationList()

            If Not AreaOfInterest.IsEmpty Then
                Select Case WebProvider.ProviderType
                    Case enumProviderType.Cuahsi, enumProviderType.Ncdc
                        tabSelect.SelectedIndex = 1
                    Case Else
                        tabSelect.SelectedIndex = 0
                End Select
            End If

        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            ShowProgress(False)
        End Try
    End Sub

    ''' <summary>
    ''' Fill datagridview and populate .txt file for possible later import
    ''' </summary>
    Private Sub FillStationList()
        Try
            If AreaOfInterest.IsEmpty Then Exit Sub
            lvNetworks.Enabled = False
            Dim LatLonWindow As New Drawing.RectangleF(txtLonMin.Text, txtLatMax.Text, txtLonMax.Text - txtLonMin.Text, txtLatMin.Text - txtLatMax.Text)
            WebProvider.dictStations.Clear()
            lblNumStations.Text = ""
            txtFilterParmName.Text = ""
            lvStations.Visible = False
            ShowProgress(True, , "Getting available stations...")
            lvStations.AddColumns("Station ID", "Station Name")

            lvParameters.Clear()
            lvSeries.Clear()

            With WebProvider
                .Initialize(dtStart.Value, dtEnd.Value, lstNCDCDataset.SelectedIndex, lstNCDCCountry.Text, lstNCDCState.Text,
                            GetSelectedNetworkIDs, GetSelectedStations, GetSelectedParameters, GetSelectedSeries)
                If UseAsyncCalls Then
                    Dim Pct As Integer = 0.0, Txt As String = ""
                    Dim th As New Threading.Thread(New Threading.ParameterizedThreadStart(AddressOf .GetStations))
                    th.Start(txtFilterStaName.Text.ToUpper)
                    While th.IsAlive
                        Threading.Thread.Sleep(200)
                        If .GetProgress(Pct, Txt) Then ShowProgress(Pct, 100, Txt)
                        If .Cancelled Then Exit Sub
                        Application.DoEvents()
                    End While
                Else
                    If Not .GetStations(txtFilterStaName.Text) Then Exit Sub
                End If

                If .LastException IsNot Nothing Then Throw .LastException

                If .dictStations.Count = 0 AndAlso .ProviderType = enumProviderType.Cuahsi AndAlso lvNetworks.CheckedItems.Count > 0 Then
                    WarningMsgFmt("No stations having data were found for the selected networks in the specified area of interest for the requested date range ({0:MM/dd/yyyy} - {1:MM/dd/yyyy}). Enlarge your area, broaden your date range, or select a different network.", dtStart.Value, dtEnd.Value)
                    Exit Sub
                End If
            End With

            For Each sta In WebProvider.dictStations.Values
                With sta
                    lvStations.AddRow(.StationID, .StationID, .StationName)
                End With
                If lvStations.Items.Count >= 1000 Then Exit For
            Next

            lblNumStations.Text = String.Format("Available Stations: {0}", WebProvider.dictStations.Count)
            If WebProvider.dictStations.Count > 1000 Then lblNumStations.Text &= String.Format(" (only {0} shown)", lvStations.Items.Count)
            With lvStations
                .CheckAll(True)
                .AutoResizeColumns(True)
                .Visible = True
                .Enabled = True
            End With
            DrawStations()
            If WebProvider.ProviderType = enumProviderType.Cuahsi Then
                FillParameterList()
                FillSeriesList()
            End If

            UpdateResultCount(True)

        Catch ex As Exception
            ErrorMsg("Unable to connect to database server; you may not have permission to view these data.", ex)
        Finally
            lvNetworks.Enabled = True
            ShowProgress(False)
        End Try
    End Sub

    ''' <summary>
    ''' Fill listview with available parameters
    ''' </summary>
    Private Sub FillParameterList()
        Try

            tvParameter.Visible = False

            With lvParameters
                .Clear()
                .AddColumns("PCode", "Parameter Name")
                .Visible = False
                .Sorting = SortOrder.Ascending
            End With

            ShowProgress(True, , "Getting parameter list...")

            With WebProvider
                .Initialize(dtStart.Value, dtEnd.Value, lstNCDCDataset.SelectedIndex, lstNCDCCountry.Text, lstNCDCState.Text,
                            GetSelectedNetworkIDs, GetSelectedStations, GetSelectedParameters, GetSelectedSeries)
                If UseAsyncCalls Then
                    Dim Pct As Integer = 0.0, Txt As String = ""
                    Dim th As New Threading.Thread(New Threading.ParameterizedThreadStart(AddressOf .GetParameters))
                    th.Start(txtFilterParmName.Text.ToUpper)
                    While th.IsAlive
                        Threading.Thread.Sleep(200)
                        If .GetProgress(Pct, Txt) Then ShowProgress(Pct, 100, Txt)
                        If .Cancelled Then Exit Sub
                        Application.DoEvents()
                    End While
                Else
                    .GetParameters(txtFilterParmName.Text)
                End If
                If .LastException IsNot Nothing Then Throw .LastException

                For Each Parm As DataWeb.clsWebProvider.clsParameter In .dictParameters.Values
                    lvParameters.AddRow(Parm.ParmID, Parm.ParmID, Parm.ParmName)
                Next
            End With

            With lvParameters
                .CheckAll(True)
                .AutoResizeColumns(True)
                If .Items.Count > 0 Then .TopItem = .Items(0)
                .Visible = True
                lblNumParms.Text = String.Format("Available Parameters: {0}", .Items.Count)
                btnOK.Enabled = .CheckedItems.Count > 0
                .ResumeLayout(True)
            End With

            UpdateResultCount(True)

        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            ShowProgress(False)
        End Try
    End Sub

    ''' <summary>
    ''' Fill series list depending on selected stations and parameters (taken from universe of available series in lat/long box that was previously determined)
    ''' </summary>
    Private Sub FillSeriesList()
        Try
            With lvSeries
                .Visible = False
                .Sorting = SortOrder.None
                .MultiSelect = False
                .FullRowSelect = True
                .HideSelectedRow = False
                .Clear()
                .AddColumns("Network", "Parameter", "Site Name")
            End With

            Dim lstSta As Generic.List(Of String) = GetSelectedStations()
            Dim lstParm As Generic.List(Of String) = GetSelectedParameters()

            For Each ser As HISCentral.SeriesRecord In WebProvider.dictSeries.Values
                With ser
                    Dim StationID As String = ser.location '.Replace(ser.ServCode & ":", "")
                    Dim PCode As String = ser.VarCode '.Replace(ser.ServCode & ":", "")

                    If lstSta.Contains(StationID) AndAlso (lstParm.Contains(PCode) OrElse lstParm.Contains(ser.conceptKeyword)) Then
                        Dim VarName As String = "" '.conceptKeyword
                        If VarName = "" Then VarName = .VarName
                        lvSeries.AddRow(ser, .ServCode, VarName, .Sitename)
                    End If
                End With
            Next
            lvSeriesInfo.Clear()
            With lvSeries
                .AutoResizeColumns(True)
                .CheckAll(True)
                .Visible = True
                lblNumSeries.Text = "Number of Series: " & .Items.Count
                .SelectedItems.Clear()
                If .Items.Count > 0 Then
                    .Items(0).Selected = True
                    UpdateSeriesDetails()
                End If
            End With

            UpdateResultCount(True)
            ShowProgress(False)
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

#End Region

#Region "GetResults routines to download data and map to WRDB..."

    Private Sub UpdateResultCount(ByVal QuickCount As Boolean)
        Try
            Application.DoEvents()

            _StationCount = lvStations.CheckedItems.Count

            lblDataCount.Text = "Unknown"

            Dim ErrMsg As String = ""
            Dim NumErr As Integer = 0

            If Not QuickCount Then ShowProgress(False)

            With WebProvider
                .Initialize(dtStart.Value, dtEnd.Value, lstNCDCDataset.SelectedIndex, lstNCDCCountry.Text, lstNCDCState.Text,
                            GetSelectedNetworkIDs, GetSelectedStations, GetSelectedParameters, GetSelectedSeries)

                .GetResultCount(QuickCount)

                If .LastException IsNot Nothing Then Throw .LastException

                'display summary stats on last tab page
                lblStationCount.Text = _StationCount
                lblParmCount.Text = lvParameters.CheckedItems.Count
                If .DataCount >= 0 Then lblDataCount.Text = .DataCount

                btnOK.Enabled = .DataCount <> 0 Or GetSelectedNetworkNames.Contains("NCDC Hourly Data")
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            If Not QuickCount Then ShowProgress(False)
        End Try
    End Sub

    Private Function GetResults() As Boolean
        Try
            ShowProgress(True, , "Retrieving data...")
            With WebProvider
                .Initialize(dtStart.Value, dtEnd.Value, lstNCDCDataset.SelectedIndex, lstNCDCCountry.Text, lstNCDCState.Text,
                            GetSelectedNetworkIDs, GetSelectedStations, GetSelectedParameters, GetSelectedSeries)
                If UseAsyncCalls Then
                    Dim Pct As Integer = 0.0, Txt As String = ""
                    Dim th As New Threading.Thread(New Threading.ParameterizedThreadStart(AddressOf .GetResults))
                    th.Start(txtFilterStaName.Text.ToUpper)
                    While th.IsAlive
                        Threading.Thread.Sleep(200)
                        If .GetProgress(Pct, Txt) Then ShowProgress(Pct, 100, Txt)
                        If .LastException IsNot Nothing Then Throw .LastException
                        If .Cancelled Then Return False
                        Application.DoEvents()
                    End While
                Else
                    If Not .GetResults() Then Return False
                End If
                If .LastException IsNot Nothing Then Throw .LastException
                lblDataCount.Text = .lstData.Count
            End With
            ShowProgress(False)
            Return GetMappedResults()
        Catch ex As Exception
            ErrorMsg(, ex)
            Return False
        Finally
            If WebProvider.ProviderType = enumProviderType.Storet Then lvParameters.Items.Clear() : tabSelect.SelectedIndex = 1 : btnOK.Enabled = False 'because it got reset during download
            ShowProgress(False)
        End Try
    End Function

    ''' <summary>
    ''' After all the requested data have been downloaded, give the user one last chance to map the Station and PCodes and suppress some variables
    ''' </summary>
    Private Function GetMappedResults() As Boolean

        If WebProvider.lstData.Count = 0 Then
            WarningMsg("No data were actually available for the stations, parameters, and date range specified.")
            Return False
        End If

        Dim dictConvStations As Generic.Dictionary(Of String, String)
        Dim dictConvPCodes As Generic.Dictionary(Of String, String())

        Dim swStation, swAgency, swData, swPCode, swValidate As IO.StreamWriter
        swStation = Nothing : swAgency = Nothing : swData = Nothing : swPCode = Nothing : swValidate = Nothing

        Try
            'do Station and PCode mapping based on data that have actually been downloaded...
            Dim lstStations As New Generic.List(Of String)
            Dim lstPCodes As New Generic.List(Of String)

            With WebProvider
                For Each dta As WRDB.Project.clsDataRecord In .lstData
                    Dim key1 As String = dta.StationID & vbTab & .dictStations(dta.StationID).StationName
                    If Not lstStations.Contains(key1) Then lstStations.Add(key1)
                    Dim ParmName As String = "", Units As String = "", Parm As clsParameter = Nothing
                    If .dictParameters.TryGetValue(dta.PCode, Parm) Then
                        ParmName = Parm.ParmName
                        Units = Parm.Units
                    Else 'add new entry
                        ParmName = dta.PCode
                        For Each s As String In .dictParameters.Keys
                            If CStr(TestNull(dta.PCode, "")).StartsWith(s) Or s.StartsWith(dta.PCode) Then 'NWIS mapping in CUAHSI not too good in that cannot always link results to parameters; if cannot, just make best guess
                                ParmName = .dictParameters(s).ParmName
                                Units = .dictParameters(s).Units
                                Exit For
                            End If
                        Next
                        .dictParameters.Add(dta.PCode, New clsParameter(dta.StationID, dta.PCode, dta.PCode, ParmName, Units, 0, Date.MinValue, Date.MaxValue))
                    End If
                    Dim key2 As String = dta.PCode & vbTab & ParmName & vbTab & Units
                    If Not lstPCodes.Contains(key2) Then lstPCodes.Add(key2)
                Next
            End With

            With New frmMapping(_IniFile, lstStations, lstPCodes, _Project)
                With .dtStart
                    .MinDate = Me.dtStart.Value
                    .MaxDate = Now
                    .Value = Me.dtStart.Value
                End With
                With .dtEnd
                    .MinDate = Me.dtStart.Value
                    .MaxDate = Max(Now, Me.dtEnd.Value)
                    .Value = Me.dtEnd.Value
                End With
                If .ShowDialog(Me) = Windows.Forms.DialogResult.Cancel Then .Dispose() : Return False
                dictConvStations = .SelectedStations
                dictConvPCodes = .SelectedParameters
                MinDate = .dtStart.Value
                MaxDate = .dtEnd.Value
                .Dispose()
            End With

            'mapping form creates dictionaries for conversion

            'this is only needed for testing...
            If _dictPCodeLookup Is Nothing Then _dictPCodeLookup = New Generic.Dictionary(Of String, String)

            If Not My.Computer.FileSystem.DirectoryExists(WRDB.Project.clsProject.TempPath) Then My.Computer.FileSystem.CreateDirectory(WRDB.Project.clsProject.TempPath)

            swData = New IO.StreamWriter(WRDB.Project.clsProject.TempPath & "\Data.txt", False)
            swData.WriteLine(StringFormat("Station_ID\tDate_Time\tPCode\tDepth_M\tCCode\tSCode\tLCode\tRCode\tResult"))

            swStation = New IO.StreamWriter(WRDB.Project.clsProject.TempPath & "\Stations.txt", False)
            swStation.WriteLine(StringFormat("Station_ID\tStation_Name\tStation_Type\tAgency\tLatitude\tLongitude\tFIPS_Code\tElevation\tRemarks"))

            swAgency = New IO.StreamWriter(WRDB.Project.clsProject.TempPath & "\Agencies.txt", False)
            swAgency.WriteLine(StringFormat("Agency\tAgency_Name"))

            swPCode = New IO.StreamWriter(WRDB.Project.clsProject.TempPath & "\PCodes.txt", False)
            swPCode.WriteLine(StringFormat("PCode\tAnal_Name\tUnits\tWQX_CharacteristicName\tWQX_ResultSampFraction\tWQX_ResultMeasureUnit"))

            swValidate = New IO.StreamWriter(WRDB.Project.clsProject.TempPath & "\Validation.txt", False)
            swValidate.WriteLine(StringFormat("Field_Name\tValid_Value"))

            'this will create a list of referenced stations and pcodes from the universe of values in the dictionaries so can write support files
            Dim lstNewStations As New Generic.List(Of String)
            Dim lstNewPCodes As New Generic.List(Of String)
            Dim lstNewAgencies As New Generic.List(Of String)

            _DataCount = 0

            'write out data values 
            For Each dta As WRDB.Project.clsDataRecord In WebProvider.lstData
                With dta
                    If dictConvStations.ContainsKey(.StationID) AndAlso dictConvPCodes.ContainsKey(.PCode) AndAlso
                        (IsDBNull(.DateTime) OrElse (.DateTime >= MinDate AndAlso .DateTime <= MaxDate)) Then
                        If Not lstNewStations.Contains(.StationID) Then lstNewStations.Add(.StationID)
                        If Not lstNewPCodes.Contains(.PCode) Then lstNewPCodes.Add(.PCode)
                        .StationID = dictConvStations(.StationID)
                        Dim Mult As Single = dictConvPCodes(.PCode)(1)
                        .PCode = dictConvPCodes(.PCode)(0).ToUpper
                        If Not IsDBNull(.Result) Then .Result *= Mult
                        If String.IsNullOrEmpty(.StationID) Or String.IsNullOrEmpty(.PCode) Then
                            Debug.Assert(False, "Invalid code")
                        Else
                            swData.WriteLine(StringFormat("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}\t{8}", .StationID, .DateTime, .PCode, TestNull(.DepthM, DBNull.Value), .CCode, .SCode, .LCode, .RCode, TestNull(.Result, "")))
                        End If
                        _DataCount += 1
                    End If
                End With
            Next

            'write out stations, pcodes, etc. referenced in selected data

            Dim lstWRDBStations As Generic.List(Of String) = _Project.DB.GetRecordList("Stations", "Station_ID")

            For i As Integer = 0 To lstNewStations.Count - 1
                Dim s As String = lstNewStations(i)
                With WebProvider.dictStations(s)
                    'If WebProvider.ProviderType = enumProviderType.Cuahsi Then
                    '    If Not ShowProgress(i, lstNewStations.Count - 1, "Getting additional station information...") Then Exit For
                    '    'following queries can take a long time...make sure they aren't already in stations table
                    '    If Not lstWRDBStations.Contains(ConvertStation(.StationID, dictConvStations)) Then
                    '        'may not have FIPS code; make low-level call to get
                    '        If String.IsNullOrEmpty(.FIPSCode) AndAlso _dictFIPS IsNot Nothing Then
                    '            Dim County As String = "", State As String = ""
                    '            With Cuahsi.GetSiteInfoObject(.SiteID, "").site(0).siteInfo
                    '                If .note IsNot Nothing Then
                    '                    For j As Integer = 0 To .note.Length - 1
                    '                        With .note(j)
                    '                            If .title.ToUpper = "COUNTY" Then County = TestNull(.Value, "")
                    '                            If .title.ToUpper = "STATE" Then State = TestNull(.Value, "") 'may be spelled out rather than two character abbrev
                    '                        End With
                    '                    Next
                    '                End If
                    '                Dim Fips As String = ""
                    '                Dim key As String = County.ToUpper & " ~ " & State.ToUpper
                    '                _dictFIPS.TryGetValue(key, Fips)
                    '            End With
                    '        End If
                    '    End If
                    swStation.WriteLine(StringFormat("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}\t{8}", dictConvStations(.StationID), .StationName, .StationType, .AgencyCode, .Latitude, .Longitude, .FIPSCode, .Elevation, .Remarks))
                    If Not lstNewAgencies.Contains(.AgencyCode) Then lstNewAgencies.Add(.AgencyCode)
                End With
            Next
            ShowProgress(False)
            _StationCount = lstNewStations.Count

            For Each s As String In lstNewAgencies
                With WebProvider.dictAgencies(s)
                    swAgency.WriteLine(StringFormat("{0}\t{1}", .AgencyCode, .AgencyName))
                End With
            Next

            Dim lstWRDBPCodes As Generic.List(Of String) = _Project.DB.GetRecordList("PCodes", "PCode")

            For Each s As String In lstNewPCodes
                With WebProvider.dictParameters(s)
                    swPCode.WriteLine(StringFormat("{0}\t{1}\t{2}\t{3}\t{4}\t{5}", dictConvPCodes(.ParmID)(0).ToUpper, .ParmName, .Units, .WQX_CharacteristicName, .WQX_ResultSampFraction, .WQX_ResultMeasureUnit))
                End With
            Next
            _PCodeCount = lstNewPCodes.Count

            For Each val As clsValidate In WebProvider.dictValidates.Values
                With val
                    swValidate.WriteLine(StringFormat("{0}\t{1}", .FieldName, .ValidValue))
                End With
            Next

            Return True

        Catch ex As Exception
            ErrorMsg(, ex)
            Return False
        Finally
            If swStation IsNot Nothing Then
                swStation.Close()
                swStation.Dispose()
            End If
            If swAgency IsNot Nothing Then
                swAgency.Close()
                swAgency.Dispose()
            End If
            If swData IsNot Nothing Then
                swData.Close()
                swData.Dispose()
            End If
            If swPCode IsNot Nothing Then
                swPCode.Close()
                swPCode.Dispose()
            End If
            If swValidate IsNot Nothing Then
                swValidate.Close()
                swValidate.Dispose()
            End If
        End Try
    End Function

#End Region

#Region "Get Selected items..."

    Private Function GetSelectedNetworkNames() As Generic.List(Of String)
        Dim lst As New Generic.List(Of String)
        For Each i As Integer In lvNetworks.CheckedIndices
            If lvNetworks.Items(i).Checked Then lst.Add(lvNetworks.Items(i).Text)
        Next
        Return lst
    End Function

    Private Function GetSelectedNetworkIDs() As Generic.List(Of Integer)
        Dim lst As New Generic.List(Of Integer)
        For Each i As Integer In lvNetworks.CheckedIndices
            lst.Add(WebProvider.dictNetworks(lvNetworks.Items(i).Tag).ServiceID)
        Next
        'If lst.Count = 0 Then lst.Add(9999)
        Return lst
    End Function

    Private Function GetSelectedParameterNames() As Generic.List(Of String)
        Dim lst As New Generic.List(Of String)
        With lvParameters
            For i As Integer = 0 To .Items.Count - 1
                If .Items(i).Checked Then
                    If Not lst.Contains(.Items(i).Text) Then lst.Add(.Items(i).Text)
                End If
            Next
        End With
        Return lst
    End Function

    ''' <summary>
    ''' Return list of checked parameters; items will be ConceptKeyword or (if empty in series), low-level varcode
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetSelectedParameters() As Generic.List(Of String)
        Dim lst As New Generic.List(Of String)
        With lvParameters
            For i As Integer = 0 To .Items.Count - 1
                If .Items(i).Checked Then
                    If Not lst.Contains(.Items(i).Text) Then lst.Add(.Items(i).Text)
                End If
            Next
        End With
        Return lst
    End Function

    Private Function GetSelectedStationNames() As Generic.List(Of String)
        Dim lst As New Generic.List(Of String)
        With lvStations
            For i As Integer = 0 To .Items.Count - 1
                If .Items(i).Checked Then
                    Dim StaID As String = .Items(i).SubItems(1).Text
                    If Not lst.Contains(StaID) Then lst.Add(StaID)
                End If
            Next
        End With
        Return lst
    End Function

    Private Function GetSelectedStations() As Generic.List(Of String)
        Dim lst As New Generic.List(Of String)
        With lvStations
            For i As Integer = 0 To .Items.Count - 1
                If .Items(i).Checked Then
                    If Not lst.Contains(.Items(i).Text) Then lst.Add(.Items(i).Text)
                End If
            Next
        End With
        Return lst
    End Function

    Private Function GetSelectedSeries() As Generic.List(Of HISCentral.SeriesRecord)
        Dim lst As New Generic.List(Of HISCentral.SeriesRecord)
        With lvSeries
            For i As Integer = 0 To .Items.Count - 1
                If .Items(i).Checked Then
                    Dim ser As HISCentral.SeriesRecord = lvSeries.Items(i).Tag
                    lst.Add(ser)
                End If
            Next
        End With
        Return lst
    End Function

#End Region

#Region "Helper functions..."

    Private Function GetPCode(ByVal i As Integer) As clsParameter
        Return WebProvider.dictParameters(lvParameters.Items(i).Text)
    End Function

    Private Function GetStation(ByVal i As Integer) As clsStation
        Return WebProvider.dictStations(lvStations.Items(i).Text)
    End Function

    Private Sub AddValidate(ByVal FieldName As String, ByVal ValidValue As String)
        Dim key As String = FieldName & " ~ " & ValidValue
        If ValidValue <> "" AndAlso Not WebProvider.dictValidates.ContainsKey(key) Then WebProvider.dictValidates.Add(key, New clsValidate(FieldName, ValidValue))
    End Sub

    Friend Sub ShowHelp(ByVal OwnerForm As Form, ByVal TopicID As String)
        'note: to assign map IDs to help topics, in RoboHelp HTML select Project tab, then Context Sensitive Help, then Aliases, then Map File WRDB.h
        Dim FileName As String = My.Application.Info.DirectoryPath & "\WRDB.chm"
        If Debugger.IsAttached Then FileName = "C:\Projects\WRDB 5.0\HELP\HTMLHelp\WRDB.chm"
        If My.Computer.FileSystem.FileExists(FileName) Then
            Help.ShowHelp(OwnerForm, FileName, HelpNavigator.TopicId, TopicID)
        Else
            WarningMsg(StringFormat("Unable to find help file named '{0}'; please restore this file or reinstall application.", FileName.Replace("\", "\\")))
        End If
    End Sub

#End Region

End Class

Public Class GMapMarkerStation
    Inherits GMapMarker

    Private _ImageName As String
    Private _ScaleFactor As Single

    Private pen As Pen

    Public Sub New(ByVal p As PointLatLng, ByVal ScaleFactor As Single, ByVal ToolTip As String, ByVal ImageName As String)
        MyBase.new(p)
        pen = New Pen(Brushes.Red)
        _ScaleFactor = ScaleFactor
        _ImageName = ImageName
        Size = New Drawing.Size(My.Resources.blue_MarkerA.Size.Width * ScaleFactor, My.Resources.blue_MarkerA.Size.Height * ScaleFactor)
        Offset = New Drawing.Point(-Size.Width / 2, -Size.Height)
        ToolTipText = ToolTip
        ToolTipMode = WindowsForms.MarkerTooltipMode.OnMouseOver
    End Sub

    Public Overrides Sub OnRender(ByVal g As System.Drawing.Graphics)
        Dim rm As Resources.ResourceManager = My.Resources.ResourceManager
        Dim img As Image = rm.GetObject(_ImageName)
        Debug.Assert(img IsNot Nothing)
        If img Is Nothing Then Exit Sub
        Dim p As Drawing.Point = MyBase.LocalPosition
        g.DrawImage(img, CSng(p.X), CSng(p.Y), img.Width * _ScaleFactor, img.Height * _ScaleFactor)
    End Sub
End Class
